package erfgame.frontend.desktop.view.terrain.iso.handler;

import java.awt.Color;
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.SwingUtilities;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;

import org.apache.batik.dom.events.EventSupport;
import org.apache.batik.swing.svg.JSVGComponent;
import org.apache.batik.swing.svg.LinkActivationEvent;
import org.apache.batik.swing.svg.LinkActivationListener;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
import org.w3c.dom.svg.SVGDocument;

import erfgame.core.Point3D;
import erfgame.core.entity.Entity;
import erfgame.core.graphics.renderer.chain.RendererChain;
import erfgame.core.world.terrain.insertion.SurfaceInsertion;
import erfgame.core.xml.ObjectXMLFactory;
import erfgame.core.xml.XmlUtil;

import erfgame.frontend.desktop.view.svg.SVGImageView;
import erfgame.frontend.desktop.view.terrain.GameTerrainView;
import erfgame.frontend.desktop.view.terrain.iso.EntitySelection;
import erfgame.frontend.desktop.view.terrain.iso.IsometricTerrainView;
import erfgame.frontend.desktop.view.terrain.iso.interaction.EntityInteractionDescriptionFactory;
import erfgame.frontend.desktop.view.terrain.iso.interaction.EntityInteractionDescriptionFactoryManager;
import erfgame.frontend.desktop.view.terrain.iso.interaction.EntityInteractionFactory;
import erfgame.frontend.desktop.view.terrain.iso.interaction.Interaction;
import erfgame.frontend.desktop.view.terrain.iso.interaction.InteractionDescription;

public class SVGMenuInteractionHandler extends ContextMenuInteractionHandler {
	
	private static final Logger log = Logger.getLogger( SVGMenuInteractionHandler.class.getName() );

	private GameTerrainView view;
	private ObjectXMLFactory<List<InteractionDescription>> xmlFactory;
	private DocumentBuilder documentBuilder;
	private Templates templates;
	private Map<String, Object> transformParameters;
	
	public SVGMenuInteractionHandler(
			GameTerrainView view, 
			EntitySelection selection,
			EntityInteractionDescriptionFactoryManager factoryManager, 
			DocumentBuilder documentBuilder,
			ObjectXMLFactory<List<InteractionDescription>> xmlFactory, 
			Templates templates
	) {
		super(view.getIsometricTerrainView(), selection, factoryManager);
		this.view = view;
		this.xmlFactory = xmlFactory;
		this.templates = templates;
		this.documentBuilder = documentBuilder;
	}
	
	public void setTransformParameters( Map<String, Object> transformParameters ) {
		this.transformParameters = transformParameters;
	}

	@Override
	public void showMenu(
			List<InteractionDescription> descriptions,
			Entity entity, 
			Point3D position, 
			int sx, int sy) {
		try {
			Transformer transformer = templates.newTransformer();
			
			int width = view.getWidth();
			int height = view.getHeight();
			
			transformer.setParameter("width", width);
			transformer.setParameter("height", height);
			transformer.setParameter("cx", sx);
			transformer.setParameter("cy", sy);
			if( transformParameters != null ) {
				for( Map.Entry<String, Object> entry : transformParameters.entrySet() ) {
					transformer.setParameter(entry.getKey(), entry.getValue());
				}					
			}
			
			SVGDocument svg = XmlUtil.transformToSVG(documentBuilder, xmlFactory, descriptions, transformer);
			
			SVGImageView layer = new SVGImageView();
//			final JSVGComponent layer = new JSVGComponent(null, true, false);
			layer.setBackground(new Color( 0x00, 0x00, 0x00, 0x33 ) );
			layer.setRecenterOnResize(true);
//			layer.setDisableInteractions(true);

			EventHandler handler = new EventHandler(
					layer, 
					descriptions, 
					entity, 
					position
			);
			
			((EventTarget)svg).addEventListener("mousedown", handler, false);
			((EventTarget)svg).addEventListener("mouseup", handler, false);
			layer.addMouseListener(handler);
			
			layer.setSize( width, height );
			layer.setSVGDocument(svg);
			view.add(layer, 0);
		} catch( Exception ex ) {
			log.log(Level.SEVERE, "unable to show context menu", ex);
		}
	}
	
	private void hide(SVGImageView layer) {
		// TODO : probably want to reuse this layer instead of creating/destroying
		layer.stopProcessing();
		layer.dispose();
		view.remove(layer);
		view.repaint();										
	}

	public String getInteractionId( EventTarget t ) {
		if( t instanceof Node ) { 
			return getInteractionId( (Node)t );
		} else {
			return null;
		}
	}
	
	public String getInteractionId( Node node ) {
		if( node != null ) {
			if( "a".equals(node.getLocalName()) ) {
				String uri = node.getAttributes().getNamedItemNS("http://www.w3.org/1999/xlink", "href").getNodeValue();
				if( uri.startsWith("i:") ) {
					return uri.substring(2);
				} else {
					return getInteractionId( node.getParentNode() );
				}
			} else {
				return getInteractionId( node.getParentNode() );
			}
		} else {
			return null;
		}
	}
	
	public InteractionDescription findDescription( List<InteractionDescription> descriptions, String id ) {
		if( descriptions != null ) {
			for( InteractionDescription description : descriptions ) {
				if( description.getId().equals(id) ) {
					return description;
				} else {
					InteractionDescription childResult = findDescription( description.getChildren(), id );
					if( childResult != null ) {
						return childResult;
					}
				}
 			}
		}
		return null;
	}
	
	private class EventHandler extends MouseAdapter implements EventListener {
		
		private SVGImageView layer;
		private String currentId;
		private boolean somethingActivated;
		private List<InteractionDescription> descriptions;
		private Entity entity;
		private Point3D position;
		
		public EventHandler( SVGImageView layer, List<InteractionDescription> descriptions, Entity entity, Point3D position ) {
			this.layer = layer;
			this.descriptions = descriptions;
			this.entity = entity;
			this.position = position;
		}
		
		@Override
		public void mousePressed(MouseEvent e) {
			somethingActivated = false;
			if( e.getButton() != MouseEvent.BUTTON1 ) {
				hide( layer );
			}
		}

		@Override
		public void mouseReleased(MouseEvent e) {
			// thread to make sure that everything else has finished up first
			Thread thread = new Thread() {
				public void run() {
					try {
						Thread.sleep(400);
					} catch( InterruptedException ex ) {
						
					}
					SwingUtilities.invokeLater(new Runnable() {
						public void run() {
							if(!somethingActivated) {
								hide(layer);
							}
						}
					});
				}
			};
			thread.start();
		}

		public void handleEvent(Event e) {
			// walk up the tree, looking for a link tag
			if( "mousedown".equals(e.getType()) ) {
				currentId = getInteractionId(e.getTarget());
			} else if( "mouseup".equals(e.getType()) || "onclick".equals(e.getType()) ) {
				String id = getInteractionId(e.getTarget());
				if( id != null ) {
					if( id.equals( currentId ) ) {
						// find the interaction description with the specified id
						InteractionDescription description = findDescription(descriptions, id);
						if( description != null ) {
							EntityInteractionFactory interactionFactory;
							interactionFactory = description.getInteractionFactory();
							if( interactionFactory != null ) {
								final Interaction interaction = interactionFactory.createInteraction(entity, position);
								
								// thread in AWT (doesn't seem to be done here)
								SwingUtilities.invokeLater(new Runnable() {
									public void run() {
										InteractionHandler handler = interaction.activate();
										if( handler != null ) {
											view.getIsometricTerrainView().addInteractionHandler(handler);
										}											
									}
								});
							}
						}
					}
				} else {
					somethingActivated = true;
				}
			}
		}
	}
}